//
//  MOS6510.m
//  C64
//
//  Created by Antonio "Willy" Malara on 19/04/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//

#import "MOS6510.h"
#import "MOS6510Opcodes.h"

char instructionSizePerMode[] = {
	1,
	3,
	3,
	2,
	2,
	1,
	2,
	2,
	2,
	3,
	3
};

@implementation MOS6510

@synthesize A, X, Y, PCL, PCH, S, P, memory;

- (id)init;
{
	if ((self = [super init]) == nil)
		return nil;
	
	S  = 0xFF;

	return self;
}

- (void)reset;
{
	self.irqMaskBit = YES;
	self.PCH        = [memory byteAtAddress:0xFFFD];
	self.PCL        = [memory byteAtAddress:0xFFFC];
}

- (void)step;
{
	unsigned short oldPC   = self.PC;
	unsigned char  opcode  = [memory byteAtAddress:self.PC];
	
	MOS6510Operand operand;
	
	switch (opcodeTable[opcode].mode)
	{
		case MOS6510AddressingModeImplied:
			operand.type = MOS6510OperandImplied;
			break;
		
		case MOS6510AddressingModeAbsolute:
			operand.type  = MOS6510OperandMemoryAddress;
			operand.value = ([memory byteAtAddress:self.PC + 2] << 8) + [memory byteAtAddress:self.PC + 1];
			break;
		
		case MOS6510AddressingModeIndexedAbsolute:
			break;
		
		case MOS6510AddressingModeIndexedZeroPage:
			operand.type  = MOS6510OperandMemoryAddress;
			operand.value = [memory byteAtAddress:self.PC + 1];
			break;
		
		case MOS6510AddressingModeRelative:
			operand.type  = MOS6510OperandMemoryAddress;
			operand.value = self.PC + instructionSizePerMode[MOS6510AddressingModeRelative] + [memory byteAtAddress:self.PC + 1];
			break;
		
		case MOS6510AddressingModeAccumulator:
			break;
		
		case MOS6510AddressingModeIndirectX:
			break;
		
		case MOS6510AddressingModeIndirectY:
			operand.type  = MOS6510OperandImmediateValue;
			operand.value = ([memory byteAtAddress:[memory byteAtAddress:self.PC + 1]] << 8) +
							 [memory byteAtAddress:[memory byteAtAddress:self.PC + 1] + 1] + Y;
			break;
		
		case MOS6510AddressingModeImmediate:
			operand.type  = MOS6510OperandImmediateValue;
			operand.value = [memory byteAtAddress:self.PC + 1];
			break;
		
		case MOS6510AddressingModeAbsoluteX:
			operand.type  = MOS6510OperandMemoryAddress;
			operand.value = ([memory byteAtAddress:self.PC + 2] << 8) + [memory byteAtAddress:self.PC + 1] + X;
			break;

		case MOS6510AddressingModeAbsoluteY:
			operand.type  = MOS6510OperandMemoryAddress;
			operand.value = ([memory byteAtAddress:self.PC + 2] << 8) + [memory byteAtAddress:self.PC + 1] + Y;
			break;			
	}
	
	SEL selector = NSSelectorFromString([NSString stringWithFormat:@"%s:", opcodeTable[opcode].name]);
	
	[self performSelector:selector withObject:(id)(&operand)];
	
	// if PC is modified, then there is a jump, do not mess with that address
	if (self.PC == oldPC)
		self.PC = self.PC + instructionSizePerMode[opcodeTable[opcode].mode];
}

- (unsigned short)PC;
{
	return (PCH << 8) + PCL;
}

- (void)setPC:(unsigned short) operand;
{
	self.PCH = (char)(operand >> 8);
	self.PCL = (char)(operand & 0xFF);
}

- (BOOL) carryBit;        { return P & 0x01; }
- (BOOL) zeroBit;         { return P & 0x02; }
- (BOOL) irqMaskBit;      { return P & 0x04; }
- (BOOL) decimalModeBit;  { return P & 0x08; }
- (BOOL) brkBit;          { return P & 0x10; }
- (BOOL) overflowBit;     { return P & 0x20; }
- (BOOL) negativeBit;     { return P & 0x40; }

- (void) setCarryBit:       (BOOL) f; { P = (f) ? (P | 0x01) : (P & (!0x01)); }
- (void) setZeroBit:        (BOOL) f; { P = (f) ? (P | 0x02) : (P & (!0x02)); }
- (void) setIrqMaskBit:     (BOOL) f; { P = (f) ? (P | 0x04) : (P & (!0x04)); }
- (void) setDecimalModeBit: (BOOL) f; { P = (f) ? (P | 0x08) : (P & (!0x08)); }
- (void) setBrkBit:         (BOOL) f; { P = (f) ? (P | 0x10) : (P & (!0x10)); }
- (void) setOverflowBit:    (BOOL) f; { P = (f) ? (P | 0x12) : (P & (!0x12)); }
- (void) setNegativeBit:    (BOOL) f; { P = (f) ? (P | 0x14) : (P & (!0x14)); }

@end
